package com.fouram.web.utils;

import javax.tools.*;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.lang.reflect.Method;
import java.net.URI;
import java.util.Arrays;


/**
 * 动态执行代码
 * @author liupeng
 */
public class InvokeCodeUtils {

	/**
	 * 运行代码
	 * @param reqParam 入参
	 * @param dynamicCode 执行的代码
	 * @param classFullName 类名，包含包路径 com.fouram.core.util.DynamicClass
	 * @throws Exception
	 */
	public static void runCode(String reqParam, String dynamicCode, String classFullName) throws Exception {
		JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
		if (compiler == null) {
			throw new IllegalStateException("Compiler not available");
		}
		// Define a file object for the source code
		InMemoryJavaFileObject fileObject = new InMemoryJavaFileObject(classFullName, dynamicCode);
		// Compile the source code
		Iterable<? extends JavaFileObject> compilationUnits = Arrays.asList(fileObject);
		ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
		JavaFileManager fileManager = new ForwardingJavaFileManager(compiler.getStandardFileManager(null, null, null)) {
			@Override
			public JavaFileObject getJavaFileForOutput(Location location, String className, JavaFileObject.Kind kind, FileObject sibling) throws IOException {
				return new SimpleJavaFileObject(URI.create("string:///" + className.replace('.', '/') + kind.extension), kind) {
					@Override
					public OutputStream openOutputStream() throws IOException {
						return outputStream;
					}
				};
			}
		};
		JavaCompiler.CompilationTask task = compiler.getTask(null, fileManager, null, null, null, compilationUnits);
		boolean compilationResult = task.call();
		if (!compilationResult) {
			throw new IllegalStateException("Compilation failed");
		}
		// Load the compiled class
		ClassLoader classLoader = new InMemoryClassLoader(outputStream.toByteArray());
		Class<?> dynamicClass = classLoader.loadClass(classFullName);
		Method executeMethod = dynamicClass.getMethod("execute", String.class);
		Object obj = dynamicClass.newInstance();
		executeMethod.invoke(obj, reqParam);
	}

	static class InMemoryJavaFileObject extends SimpleJavaFileObject {
		final String code;

		InMemoryJavaFileObject(String name, String code) {
			super(URI.create("string:///" + name.replace('.', '/') + Kind.SOURCE.extension), Kind.SOURCE);
			this.code = code;
		}

		@Override
		public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException {
			return code;
		}
	}

	static class InMemoryClassLoader extends ClassLoader {
		private final byte[] classBytes;

		InMemoryClassLoader(byte[] classBytes) {
			this.classBytes = classBytes;
		}

		@Override
		protected Class<?> findClass(String name) throws ClassNotFoundException {
			return defineClass(name, classBytes, 0, classBytes.length);
		}
	}
}
